//
//  CSNateRequest.m
//  CairnStory
//  www.cairnstory.com
//
//  Created by saturna on 11. 3. 25..
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import <CommonCrypto/CommonHMAC.h>

#import "CSNateRequest.h"
#import "CSNateRequest.h"
#import "CSNateRequestDelegate.h"
#import "CSNateOAuthResult.h"
#import "CSMultipartAttach.h"
#import "CSCommon.h"
#import "CSXMLObject.h"
#import "NSString+URLEncoding.h"
#import "NSData+Base64.h"
#import "JSON.h"



#pragma mark -
#pragma mark CSNateRequest (PrivateMethods)

@interface CSNateRequest (PrivateMethods)

- (void)requestWithMethodName:(NSString*)methodName
					andParams:(NSMutableDictionary*)params
				andHttpMethod:(NSString*)httpMethod
				 andMultipart:(BOOL)isMultipart
				andOutputType:(CSNateRequestOutputType)outputType;

- (void)requestWithURLString:(NSString*)urlString
			  andOAuthHeader:(NSString*)oauthHeader
				   andParams:(NSMutableDictionary*)params
			   andHttpMethod:(NSString*)httpMethod;

- (void)multipartRequestWithURLString:(NSString*)urlString
					   andOAuthHeader:(NSString*)oauthHeader
							andParams:(NSMutableDictionary*)params;

#pragma mark -
- (NSString*)generateOAuthSignatureWithOAuthNonce:(NSString*)oauthNonce
								andOAuthTimestamp:(NSString*)oauthTimestamp
									 andURLString:(NSString*)urlString
										andParams:(NSDictionary*)params

									 andMultipart:(BOOL)isMultipart;
- (NSString*)generateOAuthHeaderWithMehodName:(NSString*)methodName
								andOAuthNonce:(NSString*)oauthNonce
							andOAuthTimestamp:(NSString*)oauthTimestamp
							andOAuthSignature:(NSString*)oauthSignature;

#pragma mark -
- (void)appendParamToBodyData:(NSMutableData*)postBodyData
					   andKey:(NSString*)key
					 andValue:(NSString*)value;

- (void)appendAttachToBodyData:(NSMutableData*)postBodyData
						andKey:(NSString*)key
					 andAttach:(CSMultipartAttach*)attach;

- (void)failMessage:(NSString*)message
		   andError:(NSError*)error;

- (void)connectWithRequest:(NSURLRequest*)request;

- (void)requestUserProfile;

@end

// ----------------
// 미니홈피 API
NSString* const CSNateRequestRetrieveFolderList = @"http://openapi.nate.com/OApi/RestApi/CY/200110/xml_RetrieveFolderList/v1";
NSString* const CSNateRequestRetrieveMenuOpen = @"http://openapi.nate.com/OApi/RestApi/CY/200110/xml_RetrieveMenuOpen/v1";

NSString* const CSNateRequestRetrievePhotoList = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200110/xml_RetrievePhotoItemList/v1";
NSString* const CSNateRequestRetrievePhoto = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200110/xml_RetrievePhotoItem/v1";
NSString* const CSNateRequestRegisterPhotoMessage = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200110/xml_RegisterPhotoItem/v1";
NSString* const CSNateRequestUploadPhoto = @"http://openapi.nate.com/OApi/CyPhotoRestApi/V1";

NSString* const CSNateRequestRetrieveMyDiaryList = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200110/xml_RetrieveMyDiaryItemList/v1";
NSString* const CSNateRequestRetrieveMyDiary = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200110/xml_RetrieveMyDiaryItemsByDate/v1";
NSString* const CSNateRequestRegisterMyDiary = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200110/xml_RegisterMyDiaryItem/v1";

NSString* const CSNateRequestRetrieveVisitBookList = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200110/xml_RetrieveVisitBookList/v1";
NSString* const CSNateRequestRegisterVisitBook = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200110/xml_RegisterVisitBookItem/v1";

// ----------------
// 일촌 API
NSString* const CSNateRequestRetrieveOnedegListIds = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200110/xml_RetrieveOnedegListIds/v1";
NSString* const CSNateRequestRetrieveOnedegList = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200110/xml_RetrieveOnedegList/v1";
NSString* const CSNateRequestRetrieveMemProfile = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200110/xml_RetrieveMemProfile/v1";

// ----------------
// 네이트온 API
NSString* const CSNateRequestNateOnGetProfile = @"https://openapi.nate.com/OApi/RestApiSSL/ON/250020/nateon_GetProfile/v1";
NSString* const CSNateRequestNateOnUpdateNickname = @"https://openapi.nate.com/OApi/RestApiSSL/ON/250020/nateon_UpdateNickname/v1";
NSString* const CSNateRequestNateOnUpdateProfileImage = @"http://openapi.nate.com/OApi/NoMyImgRestApi/V1";

NSString* const CSNateRequestNateOnGetBuddyList = @"https://openapi.nate.com/OApi/RestApiSSL/ON/250020/nateon_GetBuddyList/v1";
NSString* const CSNateRequestNateOnGetBuddyProfiles = @"https://openapi.nate.com/OApi/RestApiSSL/ON/250020/nateon_GetBuddyProfiles/v1";
NSString* const CSNateRequestNateOnRequestBuddy = @"https://openapi.nate.com/OApi/RestApiSSL/ON/250020/nateon_RequestBuddy/v1";

NSString* const CSNateRequestNateOnSendNote = @"https://openapi.nate.com/OApi/RestApiSSL/ON/250060/nateon_SendNote/v1";
NSString* const CSNateRequestNateOnSendMail = @"https://openapi.nate.com/OApi/SendMailRestApi/ON/250040/nateon_msgsend/v1";

// ----------------
// (C)로그 API
NSString* const CSNateRequestCLogGetProfile = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200800/gethomeinfo/v1";

NSString* const CSNateRequestCLogGetNoteList = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200800/getnotelist/v1";
NSString* const CSNateRequestCLogGetNote = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200800/getnote/v1";
NSString* const CSNateRequestCLogAddNote = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200800/addnote/v1";
NSString* const CSNateRequestCLogDeleteNote = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200800/deletenote/v1";
NSString* const CSNateRequestCLogPutNoteOpenType = @"https://openapi.nate.com/OApi/RestApiSSL/CY/200800/putnoteopentype/v1";
NSString* const CSNateRequestCLogUploadPhoto = @"https://openapi.nate.com/OApi/FileApi/CY/200800/addimage/v1";

// ----------------
// 커넥팅 API
NSString* const CSNateRequestConnectCreatePost = @"https://openapi.nate.com/OApi/RestApiSSL/NC/300360/message_add/v1";
NSString* const CSNateRequestConnectSharePost = @"http://connect.nate.com/index.nate";


#pragma mark -
#pragma mark CSNateRequest implementation

@implementation CSNateRequest

@synthesize nateTag = m_nateTag;
@synthesize delegate = m_delegate;


- (id)initWithNateOAuthResult:(CSNateOAuthResult*)oauth
{
	self = [super init];
	if (self == nil)
	{
		return nil;
	}

	m_oauth = [oauth retain];

	return self;
}


- (void)dealloc
{
	[m_oauth release];
	if (m_methodName != nil)
	{
		[m_methodName release];
		m_methodName = nil;
	}

	if (m_responseData != nil)
	{
		[m_responseData release];
		m_responseData = nil;
	}

	[super dealloc];
}



#pragma mark -
#pragma mark +++++


- (void)requestWithMethodName:(NSString*)methodName
					andParams:(NSMutableDictionary*)params
				andHttpMethod:(NSString*)httpMethod
				andOutputType:(CSNateRequestOutputType)outputType
{
	[self requestWithMethodName:methodName
					  andParams:params
				  andHttpMethod:httpMethod
				   andMultipart:NO
				  andOutputType:outputType];
}

- (void)requestWithMethodName:(NSString*)methodName
					andParams:(NSMutableDictionary*)params
				andOutputType:(CSNateRequestOutputType)outputType
{
	[self requestWithMethodName:methodName
					  andParams:params
				  andHttpMethod:@"POST"
				   andMultipart:NO
				  andOutputType:outputType];
}

- (void)multipartRequestWithMethodName:(NSString*)methodName
							 andParams:(NSMutableDictionary*)params
						 andOutputType:(CSNateRequestOutputType)outputType
{
	[self requestWithMethodName:methodName
					  andParams:params
				  andHttpMethod:@"POST"
				   andMultipart:YES
				  andOutputType:outputType];
}

#pragma mark -
- (void)requestWithMethodName:(NSString*)methodName
					andParams:(NSMutableDictionary*)params
				andHttpMethod:(NSString*)httpMethod
{
	[self requestWithMethodName:methodName
					  andParams:params
				  andHttpMethod:httpMethod
				   andMultipart:NO
				  andOutputType:CSNateRequestOutputTypeXML];
}

- (void)requestWithMethodName:(NSString*)methodName
					andParams:(NSMutableDictionary*)params
{
	[self requestWithMethodName:methodName
					  andParams:params
				  andHttpMethod:@"POST"
				   andMultipart:NO
				  andOutputType:CSNateRequestOutputTypeXML];
}

- (void)multipartRequestWithMethodName:(NSString*)methodName
							 andParams:(NSMutableDictionary*)params
{
	[self requestWithMethodName:methodName
					  andParams:params
				  andHttpMethod:@"POST"
				   andMultipart:YES
				  andOutputType:CSNateRequestOutputTypeXML];
}

#pragma mark -
#pragma mark CSNateRequest (PrivateMethods)


- (void)requestWithMethodName:(NSString*)methodName
					andParams:(NSMutableDictionary*)params
				andHttpMethod:(NSString*)httpMethod
				 andMultipart:(BOOL)isMultipart
				andOutputType:(CSNateRequestOutputType)outputType
{
	[m_delegate nateRequest:self didStartWithTag:m_nateTag];
	m_outputType = outputType;

	if (m_methodName != nil)
	{
		[m_methodName release];
		m_methodName = nil;
	}
	m_methodName = [methodName retain];

	NSString* urlString = methodName;
	NSString* oauthNonce = [[CSNateOAuthResult generateOAuthNonce] retain];
	NSString* oauthTimestamp = [NSString stringWithFormat:@"%.0f", [[NSDate date] timeIntervalSince1970]];

	[params setObject:[m_oauth oauthParamForKey:kNateKeyOAuthConsumerKey] forKey:kNateKeyOAuthConsumerKey];
	[params setObject:[m_oauth oauthParamForKey:kNateKeyOAuthToken] forKey:kNateKeyOAuthToken];
	[params setObject:[m_oauth oauthParamForKey:kNateKeyOAuthSignatureMethod] forKey:kNateKeyOAuthSignatureMethod];
	[params setObject:oauthTimestamp forKey:kNateKeyOAuthTimestamp];
	[params setObject:oauthNonce forKey:kNateKeyOAuthNonce];
	[params setObject:[m_oauth oauthParamForKey:kNateKeyOAuthVersion] forKey:kNateKeyOAuthVersion];

	NSString* oauthSignature = [[self generateOAuthSignatureWithOAuthNonce:oauthNonce
														 andOAuthTimestamp:oauthTimestamp
															  andURLString:urlString
																 andParams:params
															  andMultipart:isMultipart] retain];

	[params setObject:oauthSignature forKey:kNateKeyOAuthSignature];

	NSString* oauthHeader = [[self generateOAuthHeaderWithMehodName:methodName
													  andOAuthNonce:oauthNonce
												  andOAuthTimestamp:oauthTimestamp
												  andOAuthSignature:oauthSignature] retain];


	if (DEBUG) CSLog(@"===> oauthHeader : %@", oauthHeader);

	[oauthNonce release];
	[oauthSignature release];

	if (isMultipart)
	{
		[self multipartRequestWithURLString:urlString
							 andOAuthHeader:oauthHeader
								  andParams:params];
	}
	else
	{
		[self requestWithURLString:urlString
					andOAuthHeader:oauthHeader
						 andParams:params
					 andHttpMethod:httpMethod];
	}

	[oauthHeader release];
}



- (void)requestWithURLString:(NSString*)urlString
			  andOAuthHeader:(NSString*)oauthHeader
				   andParams:(NSMutableDictionary*)params
			   andHttpMethod:(NSString*)httpMethod
{
	//if (DEBUG) CSLog(@"===> urlString : %@", urlString);
	//if (DEBUG) CSLog(@"===> params : %@", params);
	NSURL* aURL = [[NSURL alloc] initWithString:urlString];
	NSMutableURLRequest* urlRequest = [[NSMutableURLRequest alloc] initWithURL:aURL];
	[aURL release];

	[urlRequest setCachePolicy:NSURLRequestReloadIgnoringCacheData];
	[urlRequest setTimeoutInterval:kHTTPRequestTimeout];
	[urlRequest setHTTPMethod:httpMethod];
	[urlRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
	[urlRequest setValue:CurrentLanguage() forHTTPHeaderField:@"Accept-Language"];
	[urlRequest setValue:oauthHeader forHTTPHeaderField:@"Authorization"];

	NSMutableString* bodyString = [[NSMutableString alloc] init];
	NSArray* arrKey = [params allKeys];

	for (int i=0; i<arrKey.count; i++)
	{
		NSString* key = [arrKey objectAtIndex:i];
		NSObject* aValue = [params objectForKey:key];

		if ([aValue isKindOfClass:[NSArray class]])
		{
			for (NSObject* obj in ((NSArray*)aValue))
			{
				if ([bodyString length] > 0)
				{
					[bodyString appendString:@"&"];
				}
				[bodyString appendFormat:@"%@=%@", key, [[obj description] URLEncodedString]];
			}
		}
		else
		{
			if ([bodyString length] > 0)
			{
				[bodyString appendString:@"&"];
			}
			[bodyString appendFormat:@"%@=%@", key, [[aValue description] URLEncodedString]];
		}
	}

	[urlRequest setHTTPBody:[bodyString dataUsingEncoding:NSUTF8StringEncoding]];
	[bodyString release];

	[self connectWithRequest:[urlRequest autorelease]];
}



- (void)multipartRequestWithURLString:(NSString*)urlString
					   andOAuthHeader:(NSString*)oauthHeader
							andParams:(NSMutableDictionary*)params
{
	NSURL* aURL = [[NSURL alloc] initWithString:urlString];
	NSMutableURLRequest* urlRequest = [[NSMutableURLRequest alloc] initWithURL:aURL];
	[aURL release];

	[urlRequest setCachePolicy:NSURLRequestReloadIgnoringCacheData];
	[urlRequest setTimeoutInterval:kHTTPRequestTimeout];
	[urlRequest setHTTPMethod:@"POST"];

	NSString* strContentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",
								kMultipartBoundarySuffix];
	[urlRequest setValue:strContentType forHTTPHeaderField:@"Content-Type"];
	[urlRequest setValue:CurrentLanguage() forHTTPHeaderField:@"Accept-Language"];
	[urlRequest setValue:oauthHeader forHTTPHeaderField:@"Authorization"];

	NSMutableData* bodyData = [[NSMutableData alloc] init];
	NSArray* arrKey = [params allKeys];

	for (int i=0; i<arrKey.count; i++)
	{
		NSString* key = [arrKey objectAtIndex:i];
		NSObject* aValue = [params objectForKey:key];

		if ([aValue isKindOfClass:[NSArray class]])
		{
			NSArray* arrValue = (NSArray*)aValue;
			for (int k=0; k<arrValue.count; k++)
			{
				NSObject* obj = [arrValue objectAtIndex:k];
				if ([obj isKindOfClass:[CSMultipartAttach class]])
				{
					[self appendAttachToBodyData:bodyData
										  andKey:key
									   andAttach:(CSMultipartAttach*)obj];
				}
				else
				{
					[self appendParamToBodyData:bodyData
										 andKey:key
									   andValue:[obj description]];
				}
			}
		}
		else if ([aValue isKindOfClass:[CSMultipartAttach class]])
		{
			[self appendAttachToBodyData:bodyData
								  andKey:key
							   andAttach:(CSMultipartAttach*)aValue];
		}
		else
		{
			[self appendParamToBodyData:bodyData
								 andKey:key
							   andValue:[aValue description]];
		}
	}

	// terminating boundary
	if (arrKey.count > 0)
	{
		[bodyData appendData:[[NSString stringWithFormat:@"--%@--\r\n",
							   kMultipartBoundarySuffix] dataUsingEncoding:NSUTF8StringEncoding]];
	}

	//NSString* bodyString = [[NSString alloc] initWithData:bodyData encoding:NSUTF8StringEncoding];
	//if (DEBUG) CSLog(@"===> bodyString : %d / [%@]", [bodyData length], bodyString);
	//[bodyString release];

	[urlRequest setHTTPBody:bodyData];
	[bodyData release];

	[self connectWithRequest:[urlRequest autorelease]];
}




#pragma mark -


- (NSString*)generateOAuthSignatureWithOAuthNonce:(NSString*)oauthNonce
								andOAuthTimestamp:(NSString*)oauthTimestamp
									 andURLString:(NSString*)urlString
										andParams:(NSDictionary*)params
									 andMultipart:(BOOL)isMultipart
{
	// ------------------------------
	// query string
	NSMutableString* queryString = [[NSMutableString alloc] init];

	if (isMultipart)
	{
		if ([queryString length] > 0)
		{
			[queryString appendString:@"&"];
		}

		// multipart인 경우 인증 파라미터만 사용.
		// 파라미터 순서를 절대 바꾸지 말자...
		[queryString appendFormat:@"%@=%@", kNateKeyOAuthConsumerKey, [[m_oauth oauthParamForKey:kNateKeyOAuthConsumerKey] URLEncodedString]];
		[queryString appendString:@"&"];
		[queryString appendFormat:@"%@=%@", kNateKeyOAuthNonce, [oauthNonce URLEncodedString]];
		[queryString appendString:@"&"];
		[queryString appendFormat:@"%@=%@", kNateKeyOAuthSignatureMethod, [[m_oauth oauthParamForKey:kNateKeyOAuthSignatureMethod] URLEncodedString]];
		[queryString appendString:@"&"];
		[queryString appendFormat:@"%@=%@", kNateKeyOAuthTimestamp, oauthTimestamp];
		[queryString appendString:@"&"];
		[queryString appendFormat:@"%@=%@", kNateKeyOAuthToken, [[m_oauth oauthParamForKey:kNateKeyOAuthToken] URLEncodedString]];
		[queryString appendString:@"&"];
		[queryString appendFormat:@"%@=%@", kNateKeyOAuthVersion, [[m_oauth oauthParamForKey:kNateKeyOAuthVersion] URLEncodedString]];
	}
	else
	{
		// 모든 파라미터를 alphabet 순서로 정렬한다.
		NSArray* allKeys = [[params allKeys] sortedArrayUsingSelector:@selector(compare:)];
		for (NSString* key in allKeys)
		{
			NSObject* obj = [params objectForKey:key];
			if ([obj isKindOfClass:[NSArray class]])
			{
				NSArray* arr = (NSArray*)obj;
				for (NSString* value in arr)
				{
					if ([queryString length] > 0)
					{
						[queryString appendString:@"&"];
					}
					[queryString appendFormat:@"%@=%@", [key URLEncodedString], [value URLEncodedString]];
				}
			}
			else
			{
				if ([queryString length] > 0)
				{
					[queryString appendString:@"&"];
				}
				[queryString appendFormat:@"%@=%@", [key URLEncodedString], [((NSString*)obj) URLEncodedString]];
			}
		}
	}

	if (DEBUG) CSLog(@"===> queryString : [%@]", queryString);

	// ------------------------------
	// base string
	NSString* baseString = [NSString stringWithFormat:@"POST&%@&%@",
							[urlString URLEncodedString],
							[queryString URLEncodedString]];

	[queryString release];
	if (DEBUG) CSLog(@"===> baseString : [%@]", baseString);

	// ------------------------------
	// signing
	NSString* keyForSigning = [[[m_oauth oauthParamForKey:kNateKeyOAuthConsumerSecret]
								URLEncodedString] stringByAppendingString:@"&"];
	keyForSigning = [keyForSigning stringByAppendingFormat:@"%@",
					 [[m_oauth oauthParamForKey:kNateKeyOAuthTokenSecret] URLEncodedString]];


	const char *cKey  = [keyForSigning cStringUsingEncoding:NSASCIIStringEncoding];
	const char *cData = [baseString cStringUsingEncoding:NSASCIIStringEncoding];

	unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
	CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
	NSData* aHMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];

	NSString* oauthSignature = [[aHMAC base64EncodedString] retain];
	[aHMAC release];
	if (DEBUG) CSLog(@"===> oauthSignature : [%@]", oauthSignature);

	return [oauthSignature autorelease];
}


- (NSString*)generateOAuthHeaderWithMehodName:(NSString*)methodName
								andOAuthNonce:(NSString*)oauthNonce
							andOAuthTimestamp:(NSString*)oauthTimestamp
							andOAuthSignature:(NSString*)oauthSignature
{
	NSMutableString* authHeader = [[NSMutableString alloc] initWithString:@"OAuth "];

	[authHeader appendFormat:@"%@=\"%@\"", kNateKeyOAuthConsumerKey, [[m_oauth oauthParamForKey:kNateKeyOAuthConsumerKey] URLEncodedString]];
	[authHeader appendString:@","];
	[authHeader appendFormat:@"%@=\"%@\"", kNateKeyOAuthToken, [[m_oauth oauthParamForKey:kNateKeyOAuthToken]  URLEncodedString]];
	[authHeader appendString:@","];
	[authHeader appendFormat:@"%@=\"%@\"", kNateKeyOAuthSignatureMethod, [[m_oauth oauthParamForKey:kNateKeyOAuthSignatureMethod] URLEncodedString]];
	[authHeader appendString:@","];
	[authHeader appendFormat:@"%@=\"%@\"", kNateKeyOAuthSignature, [oauthSignature URLEncodedString]];
	[authHeader appendString:@","];
	[authHeader appendFormat:@"%@=\"%@\"", kNateKeyOAuthTimestamp, oauthTimestamp];
	[authHeader appendString:@","];
	[authHeader appendFormat:@"%@=\"%@\"", kNateKeyOAuthNonce, [oauthNonce URLEncodedString]];
	[authHeader appendString:@","];
	[authHeader appendFormat:@"%@=\"%@\"", kNateKeyOAuthVersion, [[m_oauth oauthParamForKey:kNateKeyOAuthVersion] URLEncodedString]];

	return [authHeader autorelease];
}





#pragma mark -

- (void)appendParamToBodyData:(NSMutableData*)postBodyData
					   andKey:(NSString*)key
					 andValue:(NSString*)value
{
	[postBodyData appendData:[[NSString stringWithFormat:@"--%@\r\n",
							   kMultipartBoundarySuffix] dataUsingEncoding:NSUTF8StringEncoding]];
	[postBodyData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key]
							  dataUsingEncoding:NSUTF8StringEncoding]];
	[postBodyData appendData:[value dataUsingEncoding:NSUTF8StringEncoding]];
	[postBodyData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}


- (void)appendAttachToBodyData:(NSMutableData*)postBodyData
						andKey:(NSString*)key
					 andAttach:(CSMultipartAttach*)attach
{
	[postBodyData appendData:[[NSString stringWithFormat:@"--%@\r\n",
							   kMultipartBoundarySuffix] dataUsingEncoding:NSUTF8StringEncoding]];
	[postBodyData appendData:[[NSString stringWithFormat:
							   @"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n",
							   key, attach.fileName] dataUsingEncoding:NSUTF8StringEncoding]];
	[postBodyData appendData:[[NSString stringWithFormat:
							   @"Content-Type: %@\r\n\r\n" ,
							   attach.contentType] dataUsingEncoding:NSUTF8StringEncoding]];
	[postBodyData appendData:attach.attachData];
	[postBodyData appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}

- (void)failMessage:(NSString*)message
		   andError:(NSError*)error
{
	[m_delegate nateRequest:self
			 didFailWithTag:m_nateTag
				withMessage:message
				  withError:error];

	[m_delegate nateRequest:self
		   didFinishWithTag:m_nateTag];
}


- (void)connectWithRequest:(NSURLRequest*)request
{
	NSURLConnection* urlConnection = [NSURLConnection connectionWithRequest:request delegate:self];
	if (urlConnection == nil)
	{
		[self failMessage:@"nate request failed!!" andError:nil];
		return;
	}

	if (m_responseData == nil)
	{
		m_responseData = [[NSMutableData data] retain];
	}
}






#pragma mark -
#pragma mark NSURLConnection delegate

- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
	m_responseCode = [((NSHTTPURLResponse*)response) statusCode];
	if (DEBUG) CSLog(@"===> responseCode : %d", m_responseCode);
//	if (m_responseCode != 200)
//	{
//		[self failMessage:@"nate request failed!!" andError:nil];
//		return;
//	}

	if (m_responseData == nil)
	{
		m_responseData = [[NSMutableData data] retain];
	}
	[m_responseData setLength:0];
}


- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
{
	[m_responseData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
	if (m_responseData == nil || m_responseData.length == 0)
	{
		[self failMessage:@"nate request failed!!" andError:nil];
		return;
	}

	if (m_responseCode != 200)
	{
		NSString* responseString = [[NSString alloc] initWithData:m_responseData encoding:NSUTF8StringEncoding];
		[m_responseData release];
		m_responseData = nil;

		//if (DEBUG) CSLog(@"=====> responseString : %@", responseString);
		NSError* error = [[NSError alloc] initWithDomain:@"nateErrDomain"
													code:m_responseCode
												userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
														  responseString, NSLocalizedDescriptionKey,
														  nil]];
		[responseString release];
		[self failMessage:@"nate request failed!!" andError:[error autorelease]];
		return;
	}

	if (CSNateRequestOutputTypeXML == m_outputType)
	{
		NSError* error = nil;
		CSXMLObject* xmlObject = [[CSXMLObject alloc] initWithXMLData:m_responseData andError:&error];
		if (DEBUG) CSLog(@"===> error : [%@]", error);
		if (error != nil)
		{
			[self failMessage:[error localizedDescription] andError:error];
			[xmlObject release];
			return;
		}

		[m_delegate nateRequest:self
			  didSuccessWithTag:m_nateTag
				 withOutputType:m_outputType
					 withResult:[xmlObject autorelease]];
	}
	else if (CSNateRequestOutputTypeJSON == m_outputType)
	{
		NSString* responseJSONString = [[NSString alloc] initWithData:m_responseData encoding:NSUTF8StringEncoding];
		[m_responseData release];
		m_responseData = nil;
		//if (DEBUG) CSLog(@"responseJSONString: %@", responseJSONString);

		NSDictionary* responseDic = [responseJSONString JSONValue];
		[responseJSONString release];

		[m_delegate nateRequest:self
			  didSuccessWithTag:m_nateTag
				 withOutputType:m_outputType
					 withResult:responseDic];
	}
	else
	{
		CSAssert1(FALSE, @"unsupported output type [%d]", m_outputType);
	}

	[m_delegate nateRequest:self
		   didFinishWithTag:m_nateTag];

}

- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
{
	[self failMessage:@"nate request failed!!" andError:error];
}




@end
